home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-03-27 | 49.6 KB | 1,152 lines | [TEXT/ROSA] |
- Common Lisp the Language, 2nd Edition
- -------------------------------------------------------------------------------
-
- 9. Declarations
-
- Declarations allow you to specify extra information about your program to the
- Lisp system. With one exception, declarations are completely optional and
- correct declarations do not affect the meaning of a correct program. The
- exception is that special declarations do affect the interpretation of variable
- bindings and references and so must be specified where appropriate. All other
- declarations are of an advisory nature, and may be used by the Lisp system to
- aid the programmer by performing extra error checking or producing more
- efficient compiled code. Declarations are also a good way to add documentation
- to a program.
-
- Note that it is considered an error for a program to violate a declaration
- (such as a type declaration), but an implementation is not required to detect
- such errors (though such detection, where feasible, is to be encouraged).
-
- -------------------------------------------------------------------------------
-
- * Declaration Syntax
- * Declaration Specifiers
- * Type Declaration for Forms
-
- -------------------------------------------------------------------------------
-
- 9.1. Declaration Syntax
-
- The declare construct is used for embedding declarations within executable
- code. Global declarations and declarations that are computed by a program are
- established by the proclaim construct.
-
- [change_begin]
- X3J13 voted in June 1989 (PROCLAIM-ETC-IN-COMPILE-FILE) to introduce the new
- macro declaim, which is guaranteed to be recognized appropriately by the
- compiler and is often more convenient than proclaim for establishing global
- declarations.
- [change_end]
-
- [Special Form]
- declare {decl-spec}*
-
- A declare form is known as a declaration. Declarations may occur only at the
- beginning of the bodies of certain special forms; that is, a declaration may
- occur only as a statement of such a special form, and all statements preceding
- it (if any) must also be declare forms (or possibly documentation strings, in
- some cases). Declarations may occur in lambda-expressions and in the forms
- listed here.
-
- define-setf-method labels
- defmacro let
- defsetf let*
- deftype locally
- defun macrolet
- do multiple-value-bind
- do* prog
- do-all-symbols prog*
- do-external-symbols with-input-from-string
- do-symbols with-open-file
- dolist with-open-stream
- dotimes with-output-to-string
- flet
-
- [change_begin]
- Notice of correction. In the first edition, the above list failed to mention
- the forms define-setf-method, with-input-from-string, with-open-file,
- with-open-stream, and with-output-to-string, even though their individual
- descriptions in the first edition specified that declarations may appear in
- those forms.
- [change_end]
-
- X3J13 voted in June 1989 (CONDITION-RESTARTS) to add with-condition-restarts
- and also (DATA-IO) to add print-unreadable-object and
- with-standard-io-syntax. The X3J13 vote left it unclear whether these macros
- permit declarations to appear at the heads of their bodies. I believe that was
- the intent, but this is only my interpretation.
-
- [change_begin]
- X3J13 voted in June 1988 (CLOS) to adopt the Common Lisp Object System, which
- includes the following additional forms in which declarations may occur:
-
- defgeneric generic-function
- define-method-combination generic-labels
- defmethod with-added-methods
- generic-flet
-
- Furthermore X3J13 voted in January 1989 (SYMBOL-MACROLET-DECLARE) to allow
- declarations to occur before the bodies of these forms:
-
- symbol-macrolet with-slots
- with-accessors
-
- There are certain aspects peculiar to symbol-macrolet (and therefore also to
- with-accessors and with-slots, which expand into uses of symbol-macrolet). An
- error is signaled if a name defined by symbol-macrolet is declared special, and
- a type declaration of a name defined by symbol-macrolet is equivalent in effect
- to wrapping a the form mentioning that type around the expansion of the defined
- symbol.
- [change_end]
-
- It is an error to attempt to evaluate a declaration. Those special forms that
- permit declarations to appear perform explicit checks for their presence.
-
- -------------------------------------------------------------------------------
- Compatibility note: In MacLisp, declare is a special form that does nothing but
- return the symbol declare as its result. The MacLisp interpreter knows nothing
- about declarations but just blindly evaluates them, effectively ignoring them.
- The MacLisp compiler recognizes declarations but processes them simply by
- evaluating the subforms of the declaration in the compilation context. In
- Common Lisp it is important that both the interpreter and compiler recognize
- declarations (especially special declarations) and treat them consistently, and
- so the rules about the structure and use of declarations have been made
- considerably more stringent. The odd tricks played in MacLisp by writing
- arbitrary forms to be evaluated within a declare form are better done in both
- MacLisp and Common Lisp by using eval-when.
- -------------------------------------------------------------------------------
-
- It is permissible for a macro call to expand into a declaration and be
- recognized as such, provided that the macro call appears where a declaration
- may legitimately appear. (However, a macro call may not appear in place of a
- decl-spec.)
-
- [change_begin]
- X3J13 voted in March 1988 (DECLARE-MACROS) to eliminate the recognition of a
- declaration resulting from the expansion of a macro call. This feature proved
- to be seldom used and awkward to implement in interpreters, compilers, and
- other code-analyzing programs.
-
- Under this change, a declaration is recognized only as such if it appears
- explicitly, as a list whose car is the symbol declare, in the body of a
- relevant special form. (Note, however, that it is still possible for a macro to
- expand into a call to the proclaim function.)
- [change_end]
-
- Each decl-spec is a list whose car is a symbol specifying the kind of
- declaration to be made. Declarations may be divided into two classes: those
- that concern the bindings of variables, and those that do not. (The special
- declaration is the sole exception: it effectively falls into both classes, as
- explained below.) Those that concern variable bindings apply only to the
- bindings made by the form at the head of whose body they appear. For example,
- in
-
- (defun foo (x)
- (declare (type float x)) ...
- (let ((x 'a)) ...)
- ...)
-
- the type declaration applies only to the outer binding of x, and not to the
- binding made in the let.
-
- -------------------------------------------------------------------------------
- Compatibility note: This represents a difference from MacLisp, in which type
- declarations are pervasive.
- -------------------------------------------------------------------------------
-
- Declarations that do not concern themselves with variable bindings are
- pervasive, affecting all code in the body of the special form. As an example of
- a pervasive declaration,
-
- (defun foo (x y) (declare (notinline floor)) ...)
-
- advises that everywhere within the body of foo the function floor should not be
- open-coded but called as an out-of-line subroutine.
-
- Some special forms contain pieces of code that, properly speaking, are not part
- of the body of the special form. Examples of this are initialization forms that
- provide values for bound variables, and the result forms of iteration
- constructs. In all cases such additional code is within the scope of any
- pervasive declarations appearing before the body of the special form.
- Non-pervasive declarations have no effect on such code, except (of course) in
- those situations where the code is defined to be within the scope of the
- variables affected by such non-pervasive declarations. For example:
-
- (defun few (x &optional (y *print-circle*))
- (declare (special *print-circle*))
- ...)
-
- The reference to *print-circle* in the first line of this example is special
- because of the declaration in the second line.
-
- (defun nonsense (k x z)
- (foo z x) ;First call to foo
- (let ((j (foo k x)) ;Second call to foo
- (x (* k k)))
- (declare (inline foo) (special x z))
- (foo x j z))) ;Third call to foo
-
- In this rather nonsensical example, the inline declaration applies to the
- second and third calls to foo, but not to the first one. The special
- declaration of x causes the let form to make a special binding for x and causes
- the reference to x in the body of the let to be a special reference. The
- reference to x in the second call to foo is also a special reference. The
- reference to x in the first call to foo is a local reference, not a special
- one. The special declaration of z causes the reference to z in the call to foo
- to be a special reference; it will not refer to the parameter to nonsense named
- z, because that parameter binding has not been declared to be special. (The
- special declaration of z does not appear in the body of the defun, but in an
- inner construct, and therefore does not affect the binding of the parameter.)
-
- [change_begin]
- X3J13 voted in January 1989 (DECLARATION-SCOPE) to replace the rules
- concerning the scope of declarations occurring at the head of a special form or
- lambda-expression:
-
- * The scope of a declaration always includes the body forms, as well as any
- ``stepper'' or ``result'' forms (which are logically part of the body), of
- the special form or lambda-expression.
-
- * If the declaration applies to a name binding, then the scope of the
- declaration also includes the scope of the name binding.
-
- Note that the distinction between pervasive and non-pervasive declarations is
- eliminated. An important change from the first edition is that
- ``initialization'' forms are specifically not included as part of the body
- under the first rule; on the other hand, in many cases initialization forms may
- fall within the scope of certain declarations under the second rule.
-
- X3J13 also voted in January 1989 (DECLARE-TYPE-FREE) to change the
- interpretation of type declarations (see section 9.2).
-
- These changes affect the interpretation of some of the examples from the first
- edition.
-
- (defun foo (x)
- (declare (type float x)) ...
- (let ((x 'a)) ...)
- ...)
-
- Under the interpretation approved by X3J13, the type declaration applies to
- both bindings of x. More accurately, the type declaration is considered to
- apply to variable references rather than bindings, and the type declaration
- refers to every reference in the body of foo to a variable named x, no matter
- to what binding it may refer.
-
- (defun foo (x y) (declare (notinline floor)) ...)
-
- This example of the use of notinline stands unchanged, but the following slight
- extension of it would change:
-
- (defun foo (x &optional (y (floor x)))
- (declare (notinline floor)) ...)
-
- Under first edition rules, the notinline declaration would be considered to
- apply to the call to floor in the initialization form for y. Under the
- interpretation approved by X3J13, the notinline would not apply to that
- particular call to floor. Instead the user must write something like
-
- (defun foo (x &optional (y (locally (declare (notinline floor))
- (floor x))))
- (declare (notinline floor)) ...)
-
- or perhaps
-
- (locally (declare (notinline floor))
- (defun foo (x &optional (y (floor x))) ...))
-
- Similarly, the special declaration in
-
- (defun few (x &optional (y *print-circle*))
- (declare (special *print-circle*))
- ...)
-
- is not considered to apply to the reference in the initialization form for y in
- few. As for the nonsense example,
-
- (defun nonsense (k x z)
- (foo z x) ;First call to foo
- (let ((j (foo k x)) ;Second call to foo
- (x (* k k)))
- (declare (inline foo) (special x z))
- (foo x j z))) ;Third call to foo
-
- under the interpretation approved by X3J13, the inline declaration is no longer
- considered to apply to the second call to foo, because it is in an
- initialization form, which is no longer considered in the scope of the
- declaration. Similarly, the reference to x in that second call to foo is no
- longer taken to be a special reference, but a local reference to the second
- parameter of nonsense.
- [change_end]
-
- [old_change_begin]
-
- [Macro]
- locally {declaration}* {form}*
-
- This macro may be used to make local pervasive declarations where desired. It
- does not bind any variables and therefore cannot be used meaningfully for
- declarations of variable bindings. (Note that the special declaration may be
- used with locally to pervasively affect references to, rather than bindings of,
- variables.) For example:
-
- (locally (declare (inline floor) (notinline car cdr))
- (declare (optimize space))
- (floor (car x) (cdr y)))
-
- [old_change_end]
-
- [change_begin]
- X3J13 voted in January 1989 (RETURN-VALUES-UNSPECIFIED) to specify that
- locally executes the forms as an implicit progn and returns the value(s) of the
- last form.
-
- X3J13 voted in March 1989 (LOCALLY-TOP-LEVEL) to make locally be a special
- form rather than a macro. It still has the same syntax.
-
- [Special Form]
- locally {declaration}* {form}*
-
- This change was made to accommodate the new compilation model for top-level
- forms in a file (see section 25.1). When a locally form appears at top level,
- the forms in its body are processed as top-level forms. This means that one
- may, for example, meaningfully use locally to wrap declarations around a defun
- or defmacro form:
-
- (locally
- (declare (optimize (safety 3) (space 3) (debug 3) (speed 1)))
- (defun foo (x &optional (y (abs x)) (z (sqrt y)))
- (bar x y z)))
-
- Without assurance that this works one must write something cumbersome such as
-
-
- (defun foo (x &optional (y (locally
- (declare (optimize (safety 3)
- (space 3)
- (debug 3)
- (speed 1)))
- (abs x)))
- (z (locally
- (declare (optimize (safety 3)
- (space 3)
- (debug 3)
- (speed 1)))
- (sqrt y))))
- (locally
- (declare (optimize (safety 3) (space 3) (debug 3) (speed 1)))
- (bar x y z)))
-
- [change_end]
-
- [Function]
- proclaim decl-spec
-
- The function proclaim takes a decl-spec as its argument and puts it into effect
- globally. (Such a global declaration is called a proclamation.) Because
- proclaim is a function, its argument is always evaluated. This allows a program
- to compute a declaration and then put it into effect by calling proclaim.
-
- Any variable names mentioned are assumed to refer to the dynamic values of the
- variable. For example, the proclamation
-
- (proclaim '(type float tolerance))
-
- once executed, specifies that the dynamic value of tolerance should always be a
- floating-point number. Similarly, any function-names mentioned are assumed to
- refer to the global function definition.
-
- A proclamation constitutes a universal declaration, always in force unless
- locally shadowed. For example,
-
- (proclaim '(inline floor))
-
- advises that floor should normally be open-coded in-line by the compiler (but
- in the situation
-
- (defun foo (x y) (declare (notinline floor)) ...)
-
- it will be compiled out-of-line anyway in the body of foo, because of the
- shadowing local declaration to that effect).
-
- [change_begin]
- X3J13 voted in January 1989 (SPECIAL-TYPE-SHADOWING) to clarify that such
- shadowing does not occur in the case of type declarations. If there is a local
- type declaration for a special variable and there is also a global proclamation
- for that same variable, then the value of the variable within the scope of the
- local declaration must be a member of the intersection of the two declared
- types. This is consistent with the treatment of nested local type declarations
- on which X3J13 also voted in January 1989 (DECLARE-TYPE-FREE) .
- [change_end]
-
- As a special case (so to speak), proclaim treats a special decl-spec as
- applying to all bindings as well as to all references of the mentioned
- variables.
-
- [change_begin]
- Notice of correction. In the first edition, this sentence referred to a
- ``special declaration-form.'' That was incorrect; proclaim accepts only a
- decl-spec, not a declaration-form.
- [change_end]
-
- For example, after
-
- (proclaim '(special x))
-
- in a function definition such as
-
- (defun example (x) ...)
-
- the parameter x will be bound as a special (dynamic) variable rather than as a
- lexical (static) variable. This facility should be used with caution. The usual
- way to define a globally special variable is with defvar or defparameter.
-
- [change_begin]
- X3J13 voted in June 1989 (PROCLAIM-ETC-IN-COMPILE-FILE) to clarify that the
- compiler is not required to treat calls to proclaim any differently from the
- way it treats any other function call. If a top-level call to proclaim is to
- take effect at compile time, it should be surrounded by an appropriate
- eval-when form. Better yet, the new macro declaim may be used instead.
-
- [Macro]
- declaim {decl-spec}*
-
- This macro is syntactically like declare and semantically like proclaim. It is
- an executable form and may be used anywhere proclaim may be called. However,
- each decl-spec is not evaluated.
-
- If a call to this macro appears at top level in a file being processed by the
- file compiler, the proclamations are also made at compile time. As with other
- defining macros, it is unspecified whether or not the compile-time side effects
- of a declaim persist after the file has been compiled (see section 25.1).
- [change_end]
-
- -------------------------------------------------------------------------------
-
- 9.2. Declaration Specifiers
-
- Here is a list of valid declaration specifiers for use in declare. A construct
- is said to be ``affected'' by a declaration if it occurs within the scope of a
- declaration.
-
- special
- (special var1 var2 ...) specifies that all of the variables named are to
- be considered special. This specifier affects variable bindings but also
- pervasively affects references. All variable bindings affected are made to
- be dynamic bindings, and affected variable references refer to the current
- dynamic binding rather than to the current local binding. For example:
-
- (defun hack (thing *mod*) ;The binding of the parameter
- (declare (special *mod*)) ; *mod* is visible to hack1,
- (hack1 (car thing))) ; but not that of thing
-
- (defun hack1 (arg)
- (declare (special *mod*)) ;Declare references to *mod*
- ; within hack1 to be special
- (if (atom arg) *mod*
- (cons (hack1 (car arg)) (hack1 (cdr arg)))))
-
- Note that it is conventional, though not required, to give special
- variables names that begin and end with an asterisk.
-
- A special declaration does not affect bindings pervasively. Inner bindings
- of a variable implicitly shadow a special declaration and must be
- explicitly re-declared to be special. (However, a special proclamation
- does pervasively affect bindings; this exception is made for reasons of
- convenience and compatibility with MacLisp.) For example:
-
- (proclaim '(special x)) ;x is always special
-
- (defun example (x y)
- (declare (special y))
- (let ((y 3) (x (* x 2)))
- (print (+ y (locally (declare (special y)) y)))
- (let ((y 4)) (declare (special y)) (foo x))))
-
- In the contorted code above, the outermost and innermost bindings of y are
- special and therefore dynamically scoped, but the middle binding is
- lexically scoped. The two arguments to + are different, one being the
- value, which is 3, of the lexically bound variable y, and the other being
- the value of the special variable named y (a binding of which happens,
- coincidentally, to lexically surround it at an outer level). All the
- bindings of x and references to x are special, however, because of the
- proclamation that x is always special.
-
- As a matter of style, use of special proclamations should be avoided. The
- defvar and defparameter macros are the conventional means for proclaiming
- special variables in a program.
-
- type
- (type type var1 var2 ...) affects only variable bindings and specifies
- that the variables mentioned will take on values only of the specified
- type. In particular, values assigned to the variables by setq, as well as
- the initial values of the variables, must be of the specified type.
-
- [change_begin]
-
- X3J13 voted in January 1989 (DECLARE-TYPE-FREE) to alter the
- interpretation of type declarations. They are not to be construed to
- affect ``only variable bindings.'' The new rule for a declaration of a
- variable to have a specified type is threefold:
-
- o It is an error if, during the execution of any reference to that
- variable within the scope of the declaration, the value of the
- variable is not of the declared type.
-
- o It is an error if, during the execution of a setq of that variable
- within the scope of the declaration, the new value for the variable
- is not of the declared type.
-
- o It is an error if, at any moment that execution enters the scope of
- the declaration, the value of the variable is not of the declared
- type.
-
- One may think of a type declaration (declare (type face bodoni)) as
- implicitly changing every reference to bodoni within the scope of the
- declaration to (the face bodoni); changing every expression exp assigned
- to bodoni within the scope of the declaration to (the face exp); and
- implicitly executing (the face bodoni) every time execution enters the
- scope of the declaration.
-
- These new rules make type declarations much more useful. Under first
- edition rules, a type declaration was useless if not associated with a
- variable binding; declarations such as in
-
- (locally
- (declare (type (byte 8) x y))
- (+ x y))
-
- at best had no effect and at worst were erroneous, depending on one's
- interpretation of the first edition. Under the interpretation approved by
- X3J13, such declarations have ``the obvious natural interpretation.''
-
- X3J13 noted that if nested type declarations refer to the same variable,
- then all of them have effect; the value of the variable must be a member
- of the intersection of the declared types.
-
- Nested type declarations could occur as a result of either macro expansion
- or carefully crafted code. There are three cases. First, the inner type
- might be a subtype of the outer one:
-
- (defun compare (apples oranges)
- (declare (type number apples oranges))
- (cond ((typep apples 'fixnum)
- ;; The programmer happens to know that, thanks to
- ;; constraints imposed by the caller, if APPLES
- ;; is a fixnum, then ORANGES will be also, and
- ;; therefore wishes to avoid the unnecessary cost
- ;; of checking ORANGES. Nevertheless the compiler
- ;; should be informed to allow it to optimize code.
- (locally (declare (type fixnum apples oranges)))
- ;; Maybe the compiler could have figured
- ;; out by flow analysis that APPLES must
- ;; be a fixnum here, but it doesn't hurt
- ;; to say it explicitly.
- (< apples oranges)))
- ((or (complex apples)
- (complex oranges))
- (error "Not yet implemented. Sorry."))
- ...))
-
- This is the case most likely to arise in code written completely by hand.
-
- Second, the outer type might be a subtype of the inner one. In this case
- the inner declaration has no additional practical effect, but it is
- harmless. This is likely to occur if code declares a variable to be of a
- very specific type and then passes it to a macro that then declares it to
- be of a less specific type.
-
- Third, the inner and outer declarations might be for types that overlap,
- neither being a subtype of the other. This is likely to occur only as a
- result of macro expansion. For example, user code might declare a variable
- to be of type integer, and a macro might later declare it to be of type
- (or fixnum package); in this case a compiler could intersect the two types
- to determine that in this instance the variable may hold only fixnums.
-
- The reader should note that the following code fragment is, perhaps
- astonishingly, not in error under the interpretation approved by X3J13:
-
- (let ((james .007)
- (maxwell 86))
- (flet ((spy-swap ()
- (rotatef james maxwell)))
- (locally (declare (integer maxwell))
- (spy-swap)
- (view-movie "The Sound of Music")
- (spy-swap)
- maxwell)))
- => 86 (after a couple of hours of Julie Andrews)
-
- The variable maxwell is declared to be an integer over the scope of the
- type declaration, not over its extent. Indeed maxwell takes on the
- non-integer value .007 while the Trapp family make their escape, but
- because no reference to maxwell within the scope of the declaration ever
- produces a non-integer value, the code is correct.
-
- Now the assignment to maxwell during the first call to spy-swap, and the
- reference to maxwell during the second call, do involve non-integer
- values, but they occur within the body of spy-swap, which is not in the
- scope of the type declaration! One could put the declaration in a
- different place so as to include spy-swap in the scope:
-
- (let ((james .007)
- (maxwell 86))
- (locally (declare (integer maxwell))
- (flet ((spy-swap ()
- (rotatef james maxwell)))
- (spy-swap) ;Bug!
- (view-movie "The Sound of Music")
- (spy-swap)
- maxwell)))
-
- and then the code is indeed in error.
-
- X3J13 also voted in January 1989 (FUNCTION-TYPE-ARGUMENT-TYPE-SEMANTICS)
- to alter the meaning of the function type specifier when used in type
- declarations (see section 4.5).
-
- [change_end]
-
- type
- (type var1 var2 ...) is an abbreviation for (type type var1 var2 ...),
- provided that type is one of the symbols appearing in table 4-1.
-
- [change_begin]
-
- Observe that this covers the particularly common case of declaring numeric
- variables:
-
- (declare (single-float mass dx dy dz)
- (double-float acceleration sum))
-
- In many implementations there is also some advantage to declaring
- variables to have certain specialized vector types such as base-string.
-
- [change_end]
-
- ftype
- (ftype type function-name-1 function-name-2 ...) specifies that the named
- functions will be of the functional type type, an example of which
- follows. For example:
-
- (declare (ftype (function (integer list) t) nth)
- (ftype (function (number) float) sin cos))
-
- Note that rules of lexical scoping are observed; if one of the functions
- mentioned has a lexically apparent local definition (as made by flet or
- labels), then the declaration applies to that local definition and not to
- the global function definition.
-
- [change_begin]
-
- X3J13 voted in March 1989 (FUNCTION-NAME) to extend ftype declaration
- specifiers to accept any function-name (a symbol or a list whose car is
- setf - see section 7.1). Thus one may write
-
- (declaim (ftype (function (list) t) (setf cadr)))
-
- to indicate the type of the setf expansion function for cadr.
-
- X3J13 voted in January 1989 (FUNCTION-TYPE-ARGUMENT-TYPE-SEMANTICS) to
- alter the meaning of the function type specifier when used in ftype
- declarations (see section 4.5).
-
- [change_end]
-
- [old_change_begin]
-
- function
- (function name arglist result-type1 result-type2 ...) is entirely
- equivalent to
-
- (ftype (function arglist result-type1 result-type2 ...) name)
-
- but may be more convenient for some purposes. For example:
-
- (declare (function nth (integer list) t)
- (function sin (number) float)
- (function cos (number) float))
-
- The syntax mildly resembles that of defun: a function-name, then an
- argument list, then a specification of results.
-
- Note that rules of lexical scoping are observed; if one of the functions
- mentioned has a lexically apparent local definition (as made by flet or
- labels), then the declaration applies to that local definition and not to
- the global function definition.
-
- [old_change_end]
-
- [change_begin]
- X3J13 voted in January 1989 (DECLARE-FUNCTION-AMBIGUITY) to remove this
- interpretation of the function declaration specifier from the language.
- Instead, a declaration specifier
-
- (function var1 var2 ...)
-
- is to be treated simply as an abbreviation for
-
- (type function var1 var2 ...)
-
- just as for all other symbols appearing in table 4-1.
-
- X3J13 noted that although function appears in table 4-1, the first edition also
- discussed it explicitly, with a different meaning, without noting whether the
- differing interpretation was to replace or augment the interpretation regarding
- table 4-1. Unfortunately there is an ambiguous case: the declaration
-
- (declare (function foo nil string))
-
- can be construed to abbreviate either
-
- (declare (ftype (function () string) foo))
-
- or
-
- (declare (type function foo nil string))
-
- The latter could perhaps be rejected on semantic grounds: it would be an error
- to declare nil, a constant, to be of type function. In any case, X3J13
- determined that the ice was too thin here; the possibility of confusion is not
- worth the convenience of an abbreviation for ftype declarations. The change
- also makes the language more consistent.
- [change_end]
-
- inline
- (inline function1 function2 ...) specifies that it is desirable for the
- compiler to open-code calls to the specified functions; that is, the code
- for a specified function should be integrated into the calling routine,
- appearing in-line in place of a procedure call. This may achieve extra
- speed at the expense of debuggability (calls to functions compiled in-line
- cannot be traced, for example). This declaration is pervasive. Remember
- that a compiler is free to ignore this declaration.
-
- Note that rules of lexical scoping are observed; if one of the functions
- mentioned has a lexically apparent local definition (as established by
- flet or labels), then the declaration applies to that local definition and
- not to the global function definition.
-
- [change_begin]
-
- X3J13 voted in October 1988 (PROCLAIM-INLINE-WHERE) to clarify that
- during compilation the inline declaration specifier serves two distinct
- purposes: it indicates not only that affected calls to the specified
- functions should be expanded in-line, but also that affected definitions
- of the specified functions must be recorded for possible use in performing
- such expansions.
-
- Looking at it the other way, the compiler is not required to save function
- definitions against the possibility of future expansions unless the
- functions have already been proclaimed to be inline. If a function is
- proclaimed (or declaimed) inline before some call to that function but the
- current definition of that function was established before the
- proclamation was processed, it is implementation-dependent whether that
- call will be expanded in-line. (Of course, it is implementation-dependent
- anyway, because a compiler is always free to ignore inline declaration
- specifiers. However, the intent of the committee is clear: for best
- results, the user is advised to put any inline proclamation of a function
- before any definition of or call to that function.)
-
- Consider these examples:
-
- (defun huey (x) (+ x 100)) ;Compiler need not remember this
- (declaim (inline huey dewey))
- (defun dewey (y) (huey (sqrt y))) ;Call to huey unlikely to be expanded
- (defun louie (z) (dewey (/ z))) ;Call to dewey likely to be expanded
-
- X3J13 voted in March 1989 (FUNCTION-NAME) to extend inline declaration
- specifiers to accept any function-name (a symbol or a list whose car is
- setf - see section 7.1). Thus one may write (declare (inline (setf cadr)))
- to indicate that the setf expansion function for cadr should be compiled
- in-line.
-
- [change_end]
-
- notinline
- (notinline function1 function2 ...) specifies that it is undesirable to
- compile the specified functions in-line. This declaration is pervasive. A
- compiler is not free to ignore this declaration.
-
- Note that rules of lexical scoping are observed; if one of the functions
- mentioned has a lexically apparent local definition (as made by flet or
- labels), then the declaration applies to that local definition and not to
- the global function definition.
-
- [change_begin]
-
- X3J13 voted in March 1989 (FUNCTION-NAME) to extend notinline
- declaration specifiers to accept any function-name (a symbol or a list
- whose car is setf - see section 7.1). Thus one may write (declare
- (notinline (setf cadr))) to indicate that the setf expansion function for
- cadr should not be compiled in-line.
-
- X3J13 voted in January 1989 (ALLOW-LOCAL-INLINE) to clarify that the
- proper way to define a function gnards that is not inline by default, but
- for which a local declaration (declare (inline gnards)) has half a chance
- of actually compiling gnards in-line, is as follows:
-
- (declaim (inline gnards))
-
- (defun gnards ...)
-
- (declaim (notinline gnards))
-
- The point is that the first declamation informs the compiler that the
- definition of gnards may be needed later for in-line expansion, and the
- second declamation prevents any expansions unless and until it is
- overridden.
-
- While an implementation is never required to perform in-line expansion,
- many implementations that do support such expansion will not process
- inline requests successfully unless definitions are written with these
- proclamations in the manner shown above.
-
- [change_end]
-
- ignore
- (ignore var1 var2 ... varn) affects only variable bindings and specifies
- that the bindings of the specified variables are never used. It is
- desirable for a compiler to issue a warning if a variable so declared is
- ever referred to or is also declared special, or if a variable is lexical,
- never referred to, and not declared to be ignored.
-
- optimize
- (optimize (quality1 value1) (quality2 value2)...) advises the compiler
- that each quality should be given attention according to the specified
- corresponding value. A quality is a symbol; standard qualities include
- speed (of the object code), space (both code size and run-time space),
- safety (run-time error checking), and compilation-speed (speed of the
- compilation process).
-
- [change_begin]
-
- X3J13 voted in October 1988 (OPTIMIZE-DEBUG-INFO) to add the standard
- quality debug (ease of debugging).
-
- [change_end]
-
- Other qualities may be recognized by particular implementations. A value
- should be a non-negative integer, normally in the range 0 to 3. The value
- 0 means that the quality is totally unimportant, and 3 that the quality is
- extremely important; 1 and 2 are intermediate values, with 1 the
- ``normal'' or ``usual'' value. One may abbreviate (quality 3) to simply
- quality. This declaration is pervasive. For example:
-
- (defun often-used-subroutine (x y)
- (declare (optimize (safety 2)))
- (error-check x y)
- (hairy-setup x)
- (do ((i 0 (+ i 1))
- (z x (cdr z)))
- ((null z) i)
- ;; This inner loop really needs to burn.
- (declare (optimize speed))
- (declare (fixnum i))
- )))
-
- declaration
- (declaration name1 name2 ...) advises the compiler that each namej is a
- valid but non-standard declaration name. The purpose of this is to tell
- one compiler not to issue warnings for declarations meant for another
- compiler or other program processor.
-
- [old_change_begin]
-
- This kind of declaration may be used only as a proclamation. For example:
-
- (proclaim '(declaration author
- target-language
- target-machine))
-
- (proclaim '(target-language ada))
-
- (proclaim '(target-machine IBM-650))
-
- (defun strangep (x)
- (declare (author "Harry Tweeker"))
- (member x '(strange weird odd peculiar)))
-
- [old_change_end]
-
- [change_begin]
-
- X3J13 voted in June 1989 (PROCLAIM-ETC-IN-COMPILE-FILE) to introduce the
- new macro declaim, which is guaranteed to be recognized appropriately by
- the compiler and is often more convenient than proclaim for establishing
- global declarations.
-
- The declaration declaration specifier may be used with declaim as well as
- proclaim. The preceding examples would be better written using declaim, to
- ensure that the compiler will process them properly.
-
- (declaim (declaration author
- target-language
- target-machine))
-
- (declaim (target-language ada)
- (target-machine IBM-650))
-
- (defun strangep (x)
- (declare (author "Harry Tweeker"))
- (member x '(strange weird odd peculiar)))
-
- X3J13 voted in March 1989 (DYNAMIC-EXTENT) to introduce a new declaration
- specifier dynamic-extent for variables, and voted in June 1989
- (DYNAMIC-EXTENT-FUNCTION) to extend it to handle function-names as well.
-
- dynamic-extent
-
- (dynamic-extent item1 item2 ... itemn) declares that certain variables or
- function-names refer to data objects whose extents may be regarded as
- dynamic; that is, the declaration may be construed as a guarantee on the
- part of the programmer that the program will behave correctly even if the
- data objects have only dynamic extent rather than the usual indefinite
- extent.
-
- Each item may be either a variable name or (function f) where f is a
- function-name (see section 7.1). (Of course, (function f) may be
- abbreviated in the usual way as #'f.)
-
- It is permissible for an implementation simply to ignore this declaration.
- In implementations that do not ignore it, the compiler (or interpreter) is
- free to make whatever optimizations are appropriate given this
- information; the most common optimization is to stack-allocate the initial
- value of the object. The data types that can be optimized in this manner
- may vary from implementation to implementation.
-
- The meaning of this declaration can be stated more precisely. We say that
- object x is an otherwise inaccessible part of y if and only if making y
- inaccessible would make x inaccessible. (Note that every object is an
- otherwise inaccessible part of itself.) Now suppose that construct c
- contains a dynamic-extent declaration for variable (or function) v (which
- need not be bound by c). Consider the values taken on by v during the
- course of some execution of c. The declaration asserts that if some object
- x is an otherwise inaccessible part of whenever becomes the value of
- v, then just after execution of c terminates x will be either inaccessible
- or still an otherwise inaccessible part of the value of v. If this
- assertion is ever violated, the consequences are undefined.
-
- In some implementations, it is possible to allocate data structures in a
- way that will make them easier to reclaim than by general-purpose garbage
- collection (for example, on the stack or in some temporary area). The
- dynamic-extent declaration is designed to give the implementation the
- information necessary to exploit such techniques.
-
- For example, in the code fragment
-
- (let ((x (list 'a1 'b1 'c1))
- (y (cons 'a2 (cons 'b2 (cons 'c2 'd2)))))
- (declare (dynamic-extent x y))
- ...)
-
- it is not difficult to prove that the otherwise inaccessible parts of x
- include the three conses constructed by list, and that the otherwise
- inaccessible parts of y include three other conses manufactured by the
- three calls to cons. Given the presence of the dynamic-extent declaration,
- a compiler would be justified in stack-allocating these six conses and
- reclaiming their storage on exit from the let form.
-
- Since stack allocation of the initial value entails knowing at the
- object's creation time that the object can be stack-allocated, it is not
- generally useful to declare dynamic-extent for variables that have no
- lexically apparent initial value. For example,
-
- (defun f ()
- (let ((x (list 1 2 3)))
- (declare (dynamic-extent x))
- ...))
-
- would permit a compiler to stack-allocate the list in x. However,
-
- (defun g (x) (declare (dynamic-extent x)) ...)
- (defun f () (g (list 1 2 3)))
-
- could not typically permit a similar optimization in f because of the
- possibility of later redefinition of g. Only an implementation careful
- enough to recompile f if the definition of g were to change incompatibly
- could stack-allocate the list argument to g in f.
-
- Other interesting cases are
-
- (declaim (inline g))
- (defun g (x) (declare (dynamic-extent x)) ...)
- (defun f () (g (list 1 2 3)))
-
- and
-
- (defun f ()
- (flet ((g (x) (declare (dynamic-extent x)) ...))
- (g (list 1 2 3))))
-
- In each case some compilers might realize the optimization is possible and
- others might not.
-
- An interesting variant of this is the so-called stack-allocated rest list,
- which can be achieved (in implementations supporting the optimization) by
-
- (defun f (&rest x)
- (declare (dynamic-extent x))
- ...)
-
- Note here that although the initial value of x is not explicitly present,
- nevertheless in the usual implementation strategy the function f is
- responsible for assembling the list for x from the passed arguments, so
- the f function can be optimized by a compiler to construct a
- stack-allocated list instead of a heap-allocated list.
-
- Some Common Lisp functions take other functions as arguments; frequently
- the argument function is a so-called downward funarg, that is, a
- functional argument that is passed only downward and whose extent may
- therefore be dynamic.
-
- (flet ((gd (x) (atan (sinh x))))
- (declare (dynamic-extent #'gd)) ;mapcar won't hang on to gd
- (mapcar #'gd my-list-of-numbers))
-
- The following three examples are in error, since in each case the value of
- x is used outside of its extent.
-
- (length (let ((x (list 1 2 3)))
- (declare (dynamic-extent x))
- x)) ;Wrong
-
- The preceding code is obviously incorrect, because the cons cells making
- up the list in x might be deallocated (thanks to the declaration) before
- length is called.
-
- (length (list (let ((x (list 1 2 3)))
- (declare (dynamic-extent x))
- x))) ;Wrong
-
- In this second case it is less obvious that the code is incorrect, because
- one might argue that the cons cells making up the list in x have no effect
- on the result to be computed by length. Nevertheless the code briefly
- violates the assertion implied by the declaration and is therefore
- incorrect. (It is not difficult to imagine a perfectly sensible
- implementation of a garbage collector that might become confused by a cons
- cell containing a dangling pointer to a list that was once stack-allocated
- but then deallocated.)
-
- (progn (let ((x (list 1 2 3)))
- (declare (dynamic-extent x))
- x) ;Wrong
- (print "Six dollars is your change have a nice day NEXT!"))
-
- In this third case it is even less obvious that the code is incorrect,
- because the value of x returned from the let construct is discarded right
- away by the progn. Indeed it is, but ``right away'' isn't fast enough. The
- code briefly violates the assertion implied by the declaration and is
- therefore incorrect. (If the code is being interpreted, the interpreter
- might hang on to the value returned by the let for some time before it is
- eventually discarded.)
-
- Here is one last example, one that has little practical import but is
- theoretically quite instructive.
-
- (dotimes (j 10)
- (declare (dynamic-extent j))
- (setq foo 3) ;Correct
- (setq foo j)) ;Erroneous-but why? (see text)
-
- Since j is an integer by the definition of dotimes, but eq and eql are not
- necessarily equivalent for integers, what are the otherwise inaccessible
- parts of j, which this declaration requires the body of the dotimes not to
- ``save''? If the value of j is 3, and the body does (setq foo 3), is that
- an error? The answer is no, but the interesting thing is that it depends
- on the implementation-dependent behavior of eq on numbers. In an
- implementation where eq and eql are equivalent for 3, then 3 is not an
- otherwise inaccessible part because (eq j (+ 2 1)) is true, and therefore
- there is another way to access the object besides going through j. On the
- other hand, in an implementation where eq and eql are not equivalent for
- 3, then the particular 3 that is the value of j is an otherwise
- inaccessible part, but any other 3 is not. Thus (setq foo 3) is valid but
- (setq foo j) is erroneous. Since (setq foo j) is erroneous in some
- implementations, it is erroneous in all portable programs, but some other
- implementations may not be able to detect the error. (If this conclusion
- seems strange, it may help to replace 3 everywhere in the preceding
- argument with some obvious bignum such as 375374638837424898243 and to
- replace 10 with some even larger bignum.)
-
- The dynamic-extent declaration should be used with great care. It makes
- possible great performance improvements in some situations, but if the
- user misdeclares something and consequently the implementation returns a
- pointer into the stack (or stores it in the heap), an undefined situation
- may result and the integrity of the Lisp storage mechanism may be
- compromised. Debugging these situations may be tricky. Users who have
- asked for this feature have indicated a willingness to deal with such
- problems; nevertheless, I do not encourage casual users to use this
- declaration.
-
- [change_end]
-
- An implementation is free to support other (implementation-dependent)
- declaration specifiers as well. On the other hand, a Common Lisp compiler is
- free to ignore entire classes of declaration specifiers (for example,
- implementation-dependent declaration specifiers not supported by that
- compiler's implementation), except for the declaration declaration specifier.
- Compiler implementors are encouraged, however, to program the compiler to issue
- by default a warning if the compiler finds a declaration specifier of a kind it
- never uses. Such a warning is required in any case if a declaration specifier
- is not one of those defined above and has not been declared in a declaration
- declaration.
-
- -------------------------------------------------------------------------------
-
- 9.3. Type Declaration for Forms
-
- Frequently it is useful to declare that the value produced by the evaluation of
- some form will be of a particular type. Using declare one can declare the type
- of the value held by a bound variable, but there is no easy way to declare the
- type of the value of an unnamed form. For this purpose the the special form is
- defined; (the type form) means that the value of form is declared to be of type
- type.
-
- [Special Form]
- the value-type form
-
- The form is evaluated; whatever it produces is returned by the the form. In
- addition, it is an error if what is produced by the form does not conform to
- the data type specified by value-type (which is not evaluated). (A given
- implementation may or may not actually check for this error. Implementations
- are encouraged to make an explicit error check when running interpretively.) In
- effect, this declares that the user undertakes to guarantee that the values of
- the form will always be of the specified type. For example:
-
- (the string (copy-seq x)) ;The result will be a string
- (the integer (+ x 3)) ;The result of + will be an integer
- (+ (the integer x) 3) ;The value of x will be an integer
- (the (complex rational) (* z 3))
- (the (unsigned-byte 8) (logand x mask))
-
- The values type specifier may be used to indicate the types of multiple values:
-
- (the (values integer integer) (floor x y))
- (the (values string t)
- (gethash the-key the-string-table))
-
- [change_begin]
- X3J13 voted in June 1989 (THE-AMBIGUITY) to clarify that value-type may be
- any valid type specifier whatsoever. The point is that a type specifier need
- not be one suitable for discrimination but only for declaration.
-
- In the case that the form produces exactly one value and value-type is not a
- values type specifier, one may describe a the form as being entirely equivalent
- to
-
- (let ((#1=#:temp form)) (declare (type value-type #1#)) #1#)
-
- A more elaborate expression could be written to describe the case where
- value-type is a values type specifier.
- [change_end]
-
- -------------------------------------------------------------------------------
- Compatibility note: This construct is borrowed from the Interlisp DECL package;
- Interlisp, however, allows an implicit progn after the type specifier rather
- than just a single form. The MacLisp fixnum-identity and flonum-identity
- constructs can be expressed as (the fixnum x) and (the single-float x).
- -------------------------------------------------------------------------------
-
-
-
-
-